<?php


namespace App\Service;

use Yaconf;

class Dict {


    protected $lang = DictConstant::LANG_ZH;
    protected $encode = DictConstant::ENCODE_BASE64;

    /**
     * 设置字典语言
     * @param string $lang
     */
    public function setLang($lang = DictConstant::LANG_ZH) {
        $this->lang = $lang == DictConstant::LANG_EN ? DictConstant::LANG_EN : DictConstant::LANG_ZH;
    }

    /**
     * 获取字典所有内容
     * @param $ddType
     * @return bool|array
     */
    public function all($ddType) {
        $ddType .= '_' . $this->lang;
        return $this->getDictValue($ddType);
    }

    /**
     * 获取code的翻译值
     * @param string $ddType 字典类型
     * @param string $code 字典code
     * @param string $default 默认结果
     * @param string $multiCodeDelimiter 多code字段分隔符 如地区  010000,020000
     * @param string $multiValueDelimiter 多code翻译值结果分隔符  北京,上海 北京/上海
     * @return string
     */
    public function trans($ddType, string $code, string $default = '', string $multiCodeDelimiter = ',', string $multiValueDelimiter = ',') {
        $ddType .= '_' . $this->lang;
        if (!empty($multiCodeDelimiter) && strpos($code, $multiCodeDelimiter) > 0) {
            $codes = explode($multiCodeDelimiter, $code);
            $codeValues = [];
            foreach ($codes as $item) {
                $item = trim($item);
                if (strlen($item) > 0) {
                    $itemValue = $this->getDictValue($ddType . '.' . $item);
                    if (!is_null($itemValue)) {
                        $codeValues[] = $itemValue;
                    }
                }
            }
            if (empty($codeValues)) {
                $value = $default;
            } else {
                $value = implode($multiValueDelimiter, $codeValues);
            }
        } else {
            if (strlen($code) > 0) {
                $codeValue = $this->getDictValue($ddType . '.' . $code);
            } else {
                $codeValue = null;
            }

            $value = is_null($codeValue) ? $default : $codeValue;
        }

        return $value;
    }

    /**
     * 多条记录，二维数组字典值翻译
     * @param $rows
     * @param array $attributes 字段名  数组格式  [fieldName1 => [ddType1, defaultVal1], fieldName2 => ddType2] 这样的形式
     * @param bool $saveCode
     * @param string $multiCodeDelimiter 多code字段分隔符
     * @param string $multiValueDelimiter
     * @param callable $filter 过滤器回调，第一个参数为字段名，第二个参数为字段内容，仅当返回true(truthy)时进行翻译；null则全部翻译。
     * @return bool
     */
    public function rowTrans(&$rows, array $attributes, bool $saveCode = true, string $multiCodeDelimiter = ',', string $multiValueDelimiter = ',', callable $filter = null) {
        if ($rows instanceof \Generator) {
            return false;
        }
        if (count($rows) == count($rows, 1)) {
            //是一维数据
            return $this->singRowTrans($rows, $attributes, $saveCode, $multiCodeDelimiter, $multiValueDelimiter, $filter);
        }
        foreach ($rows as &$row) {
            $this->singRowTrans($row, $attributes, $saveCode, $multiCodeDelimiter, $multiValueDelimiter, $filter);
        }
    }

    /**
     * 单条记录，一维数组字典值翻译
     * @param $row      记录数组
     * @param array $attributes 字段名  数组格式  [fieldName1 => [ddType1, defaultVal1], fieldName2 => ddType2] 这样的形式
     * @param bool $saveCode 是否保存字典值
     * @param string $multiCodeDelimiter 多code字段分隔符
     * @param string $multiValueDelimiter
     * @param callable $filter 过滤器回调，第一个参数为字段名，第二个参数为字段内容，仅当返回true(truthy)时进行翻译；null则全部翻译。
     * @return void
     */
    public function singRowTrans(&$row, array $attributes, bool $saveCode = true, string $multiCodeDelimiter = ',', string $multiValueDelimiter = ',', callable $filter = null) {
        $hasFilter = !empty($filter) && is_callable($filter);
        foreach ($attributes as $attribute => $item) {
            if (!isset($row[$attribute]) || $row[$attribute] === '') {
                continue;
            }
            if ($hasFilter && !$filter($attribute, $row[$attribute])) continue; //过滤器回调返回false则不翻译。
            if (is_array($item)) {
                $ddType = $item[0];
                $default = isset($item[1]) ? (string)$item[1] : '';
            } else {
                $ddType = $item;
                $default = '';
            }
            $value = $this->trans($ddType, $row[$attribute], $default, $multiCodeDelimiter, $multiValueDelimiter);
            if ($saveCode) {
                $row[$attribute . '_code'] = $row[$attribute];
            }
            $row[$attribute] = $value;
        }
    }

    /**
     * 从字典中根据字典code获取字典值, 并根据编码结果进行自动解码
     * @param string $key
     * @return bool|string
     * @throws \Exception
     */
    protected function getDictValue(string $key) {
        $key = $this->getMapKey($key);
        $value = Yaconf::get($key);
        if (is_null($value)) {
            return null;
        }
        switch ($this->encode) {
            case DictConstant::ENCODE_BASE64:
                if (is_string($value)) {
                    $value = base64_decode($value);
                } else {
                    foreach ($value as &$item) {
                        $item = base64_decode($item);
                    }
                }
                return $value === false ? null : $value;
            default;
                return $value;
        }
    }

    /**
     *
     * @param $key
     * @return mixed
     * @throws \Exception
     */
    protected function getMapKey($key) {
        $dictVer = config('app.dictmapver');
        if (empty($dictVer)) {
            return $key;
        }
        if (strpos($key, '.') !== false) {
            list($dd, $code) = explode('.', $key);
            list($ddType, $lang) = explode('_', $dd);
            $mapFile = rtrim(config('app.dict_path'), '/') . '/map/' . $dictVer . '/' . $ddType . '.php';
            if (is_file($mapFile)) {
                $mapCode = include $mapFile;
                $code = $mapCode[$code] ?? $code;
                return $ddType . '_' . $lang . '.' . $code;
            }
        }
        return $key;
    }
}